<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>良工装饰 - 私域运营项目进度管理看板 (V22 - Firebase 实时协作)</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
    <!-- SheetJS library for Excel handling -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
    
    <!-- Firebase SDK -->
    <script type="module">
        // Import the functions you need from the SDKs you need
        import { initializeApp } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-app.js";
        import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-auth.js";
        import { getFirestore, doc, onSnapshot, setDoc, getDoc } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-firestore.js";
        window.firebase = {
            initializeApp,
            getAuth,
            signInAnonymously,
            onAuthStateChanged,
            signInWithCustomToken,
            getFirestore,
            doc,
            onSnapshot,
            setDoc,
            getDoc
        };
    </script>

    <style>
        html, body { scroll-behavior: smooth; }
        body {
            font-family: 'Inter', 'Noto Sans SC', sans-serif;
            background-color: #f7f8fc;
        }
        .task-card h3 {
            cursor: pointer;
        }
        .task-item-title {
            cursor: default;
        }
        .progress-bar-bg { background-color: #e9ecef; }
        .progress-bar-fill { transition: width 0.5s ease-in-out; }
        #card-details-modal, #password-modal, #loading-overlay { transition: opacity 0.3s ease-in-out; }

        /* --- Modal (Card Details) Gantt Chart Styles --- */
        #card-details-modal .gantt-chart-container {
            overflow-x: auto;
            background-color: white;
            border-radius: 0.5rem;
            border: 1px solid #e5e7eb;
        }
        #card-details-modal .gantt-timeline {
            display: grid;
            grid-template-columns: repeat(auto-fill, 40px);
            white-space: nowrap;
        }
        #card-details-modal .gantt-day {
            text-align: center;
            padding: 8px 0;
            font-size: 0.75rem;
            border-right: 1px solid #e5e7eb;
            border-bottom: 1px solid #e5e7eb;
            color: #4b5563;
        }
        #card-details-modal .gantt-day.weekend { background-color: #f3f4f6; }
        #card-details-modal .gantt-row {
            height: 44px;
            border-bottom: 1px solid #e5e7eb;
            position: relative;
        }
        #card-details-modal .gantt-row:last-child { border-bottom: none; }
        #card-details-modal .gantt-bar {
            position: absolute;
            height: 28px;
            top: 50%;
            transform: translateY(-50%);
            background-color: #3b82f6;
            border-radius: 4px;
            color: white;
            font-size: 0.75rem;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
            padding: 0 8px;
            cursor: pointer;
            transition: background-color 0.2s;
            display: flex;
            align-items: center;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        #card-details-modal .gantt-bar:hover { background-color: #2563eb; }
        #card-details-modal .gantt-bar-status-completed { background-color: #22c55e; }
        #card-details-modal .gantt-bar-status-completed:hover { background-color: #16a34a; }
        #card-details-modal .gantt-bar-status-overdue { background-color: #ef4444; }
        #card-details-modal .gantt-bar-status-overdue:hover { background-color: #dc2626; }
        #card-details-modal .gantt-today-marker {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 2px;
            background-color: #ef4444;
            z-index: 15;
            pointer-events: none;
        }
        #card-details-modal .task-editor-grid {
            display: grid;
            grid-template-columns: 2fr 1fr 1.5fr 1fr auto;
            gap: 0.75rem;
            align-items: center;
            padding: 0.5rem 0.75rem;
            border-bottom: 1px solid #e5e7eb;
        }
        #card-details-modal .task-editor-grid:last-child { border-bottom: none; }

        /* --- Master (Overview) Gantt Chart Styles --- */
        #master-gantt-chart {
            overflow-x: auto;
        }
        #master-gantt-chart .master-gantt-grid {
            display: grid;
            grid-template-columns: 200px 1fr;
        }
        #master-gantt-chart .master-gantt-timeline {
            display: grid;
            grid-template-columns: repeat(auto-fill, 20px);
            white-space: nowrap;
        }
        #master-gantt-chart .master-gantt-day {
            text-align: center;
            padding: 4px 0;
            font-size: 10px;
            border-right: 1px solid #e5e7eb;
            border-bottom: 1px solid #e5e7eb;
        }
        #master-gantt-chart .master-gantt-day.weekend { background-color: #f3f4f6; }
        #master-gantt-chart .master-gantt-row {
            height: 40px;
            border-bottom: 1px solid #e5e7eb;
            position: relative;
        }
        #master-gantt-chart .master-gantt-label {
            height: 40px;
            border-bottom: 1px solid #e5e7eb;
            border-right: 1px solid #e5e7eb;
            background-color: white;
            display: flex;
            align-items: center;
            padding: 0 0.5rem;
            font-size: 0.875rem;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
        #master-gantt-chart .master-gantt-bar {
            position: absolute;
            height: 24px;
            top: 8px;
            background-color: #3b82f6;
            border-radius: 4px;
            color: white;
            font-size: 0.75rem;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
            padding: 0 8px;
            cursor: pointer;
            transition: background-color 0.2s;
            display: flex;
            align-items: center;
        }
        #master-gantt-chart .master-gantt-bar:hover { background-color: #2563eb; }
        #master-gantt-chart .master-gantt-bar-phase {
            height: 12px;
            top: 14px;
            opacity: 0.7;
        }
        #master-gantt-chart .master-gantt-label-phase {
            font-weight: bold;
            background-color: #f3f4f6;
        }
        #master-gantt-chart .master-gantt-label-card {
            padding-left: 20px !important;
        }
    </style>
</head>
<body class="p-4 md:p-8">

    <!-- Loading Overlay -->
    <div id="loading-overlay" class="fixed inset-0 bg-white z-[200] flex flex-col justify-center items-center">
        <svg class="animate-spin h-10 w-10 text-indigo-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
            <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
            <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
        </svg>
        <p class="mt-4 text-gray-600">正在连接实时数据库...</p>
    </div>

    <div id="app-container" class="max-w-7xl mx-auto">
        <!-- Header -->
        <header class="mb-8 p-6 bg-white rounded-xl shadow-lg border border-gray-200 relative">
            <div class="flex flex-col md:flex-row justify-between items-start">
                <div>
                    <h1 class="text-3xl font-bold text-gray-900">良工装饰</h1>
                    <p class="text-lg text-gray-600">私域运营项目进度管理看板 (实时协作版)</p>
                </div>
                <div class="mt-4 md:mt-0 flex flex-col items-end gap-4">
                     <div class="grid grid-cols-2 gap-x-8 items-center">
                         <p class="text-sm font-medium text-gray-500 text-right">项目总天数</p>
                         <p class="text-sm font-medium text-gray-500">项目总进度</p>
                         <p id="total-duration-text" class="text-3xl font-bold text-indigo-600 text-right">- 天</p>
                         <div class="w-full md:w-56">
                             <div class="w-full progress-bar-bg rounded-full h-4">
                                 <div id="total-progress-bar" class="bg-gradient-to-r from-sky-500 to-indigo-500 h-4 rounded-full progress-bar-fill" style="width: 0%;"></div>
                             </div>
                             <p id="total-progress-text" class="text-center text-sm font-semibold text-indigo-700 mt-1">0%</p>
                         </div>
                     </div>
                     <div class="flex gap-2">
                         <button id="import-excel-btn" class="bg-gray-100 text-gray-700 px-3 py-1.5 rounded-md text-sm font-medium hover:bg-gray-200 flex items-center gap-1.5">
                            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M9 8.25H7.5a2.25 2.25 0 0 0-2.25 2.25v9a2.25 2.25 0 0 0 2.25 2.25h9a2.25 2.25 0 0 0 2.25-2.25v-9a2.25 2.25 0 0 0-2.25-2.25H15m0-3-3-3m0 0-3 3m3-3v11.25" /></svg>
                             导入 Excel
                         </button>
                         <input type="file" id="excel-file-input" class="hidden" accept=".xlsx, .xls">
                         <button id="export-excel-btn" class="bg-green-600 text-white px-3 py-1.5 rounded-md text-sm font-medium hover:bg-green-700 flex items-center gap-1.5">
                            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" /></svg>
                             导出 Excel
                         </button>
                         <button id="reset-data-btn" class="bg-red-500 text-white px-3 py-1.5 rounded-md text-sm font-medium hover:bg-red-600 flex items-center gap-1.5">
                            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 11.667 0l3.181-3.183m-4.991-2.691V5.25a2.25 2.25 0 0 0-2.25-2.25h-4.5a2.25 2.25 0 0 0-2.25 2.25v4.992m11.667 0-3.181 3.183a8.25 8.25 0 0 1-11.667 0l-3.181-3.183" /></svg>
                             重置所有数据
                         </button>
                     </div>
                </div>
            </div>
        </header>

        <!-- Master Gantt Chart Section -->
        <section id="master-gantt-section" class="mb-8 p-6 bg-white rounded-xl shadow-lg border border-gray-200">
            <h2 class="text-2xl font-bold text-gray-800 mb-4">项目总览甘特图</h2>
            <div id="master-gantt-chart" class="overflow-x-auto"></div>
        </section>

        <!-- Q&A Section -->
        <section id="qa-section" class="mb-8 p-6 bg-white rounded-xl shadow-lg border border-gray-200">
             <div class="flex justify-between items-center mb-4">
                 <h2 class="text-2xl font-bold text-gray-800">项目疑问区</h2>
                 <div class="flex items-center gap-2">
                     <input type="text" id="qa-search-input" placeholder="搜索疑问..." class="border-gray-300 rounded-md shadow-sm text-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
                     <select id="qa-filter-select" class="border-gray-300 rounded-md shadow-sm text-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
                         <option value="all">全部</option>
                         <option value="unresolved">未解决</option>
                         <option value="resolved">已解决</option>
                     </select>
                     <button id="export-qa-btn" class="bg-blue-500 text-white px-3 py-1.5 rounded-md text-sm font-medium hover:bg-blue-600 flex items-center gap-1.5">
                         <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" /></svg>
                         导出
                     </button>
                 </div>
             </div>
             <div id="qa-list" class="space-y-4"></div>
             <div id="qa-pagination" class="mt-4 flex justify-center items-center gap-2"></div>
             <div id="add-qa-container" class="mt-6">
                 <button id="add-qa-btn" class="bg-indigo-100 text-indigo-700 px-4 py-2 rounded-md text-sm font-medium hover:bg-indigo-200">提出新疑问</button>
             </div>
        </section>

        <!-- Main Content -->
        <main id="project-board" class="space-y-8"></main>
    </div>

    <!-- Card Details Modal with Gantt Chart -->
    <div id="card-details-modal" class="hidden fixed inset-0 bg-black bg-opacity-60 z-50 flex justify-center items-center p-4">
        <div class="bg-gray-50 rounded-lg shadow-2xl w-full max-w-7xl max-h-[90vh] flex flex-col">
            <div class="p-5 border-b border-gray-200 flex justify-between items-center bg-white rounded-t-lg">
                <h3 id="modal-card-title" class="text-xl font-bold text-gray-800">Card Title</h3>
                <button id="modal-card-close-btn" class="p-1 rounded-full hover:bg-gray-200">
                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-gray-600"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" /></svg>
                </button>
            </div>
            <div id="modal-content-area" class="overflow-y-auto flex-grow p-4 sm:p-6">
                <!-- Detailed Gantt will be rendered here -->
            </div>
        </div>
    </div>

    <!-- Password Modal -->
    <div id="password-modal" class="hidden fixed inset-0 bg-black bg-opacity-70 z-[100] flex justify-center items-center p-4">
        <div class="bg-white rounded-lg shadow-2xl p-6 w-full max-w-sm">
            <h3 id="password-modal-title" class="text-lg font-bold text-gray-800 mb-4">请输入密码</h3>
            <p id="password-error" class="text-sm text-red-500 mb-2 hidden">密码错误！</p>
            <form id="password-form">
                <input type="password" id="password-input" class="w-full border-gray-300 rounded-md shadow-sm" required>
                <div class="flex justify-end gap-3 mt-4">
                    <button type="button" id="password-cancel-btn" class="bg-gray-200 text-gray-800 px-4 py-2 rounded-md text-sm font-medium hover:bg-gray-300">取消</button>
                    <button type="submit" id="password-confirm-btn" class="bg-red-600 text-white px-4 py-2 rounded-md text-sm font-medium">确认</button>
                </div>
            </form>
        </div>
    </div>
    
    <script type="module">
    // 等待 DOM 加载完毕
    document.addEventListener('DOMContentLoaded', async function () {
        // 从 window 对象中获取 Firebase 函数
        const { initializeApp, getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken, getFirestore, doc, onSnapshot, setDoc, getDoc } = window.firebase;

        // --- 全局变量和状态 ---
        let projectData = {};
        let qaData = [];
        let qaCurrentPage = 1;
        const qaItemsPerPage = 8;
        const DELETE_PASSWORD = "123456";
        const statusColors = {'not-started': 'border-gray-400','in-progress': 'border-blue-500','completed': 'border-green-500','blocked': 'border-red-500'};
        const ganttBarColors = {'not-started': 'bg-blue-500','in-progress': 'bg-yellow-500','completed': 'bg-green-500','blocked': 'bg-red-500'};

        // --- DOM 元素获取 ---
        const loadingOverlay = document.getElementById('loading-overlay');
        const projectBoard = document.getElementById('project-board');
        const cardModal = document.getElementById('card-details-modal');
        const modalCardCloseBtn = document.getElementById('modal-card-close-btn');
        const modalCardTitle = document.getElementById('modal-card-title');
        const modalContentArea = document.getElementById('modal-content-area');
        const passwordModal = document.getElementById('password-modal');
        const passwordModalTitle = document.getElementById('password-modal-title');
        const passwordForm = document.getElementById('password-form');
        const passwordInput = document.getElementById('password-input');
        const passwordError = document.getElementById('password-error');
        const passwordCancelBtn = document.getElementById('password-cancel-btn');
        const passwordConfirmBtn = document.getElementById('password-confirm-btn');
        const exportBtn = document.getElementById('export-excel-btn');
        const importBtn = document.getElementById('import-excel-btn');
        const fileInput = document.getElementById('excel-file-input');
        const resetDataBtn = document.getElementById('reset-data-btn');
        const masterGanttContainer = document.getElementById('master-gantt-chart');
        const qaSection = document.getElementById('qa-section');
        const qaList = document.getElementById('qa-list');
        const addQAContainer = document.getElementById('add-qa-container');
        const qaSearchInput = document.getElementById('qa-search-input');
        const qaFilterSelect = document.getElementById('qa-filter-select');
        const qaPagination = document.getElementById('qa-pagination');
        const exportQaBtn = document.getElementById('export-qa-btn');

        // --- Firebase 初始化 ---
        let db, auth, userId;
        let dataUnsubscribe = null; // 用于取消监听

        // 使用由环境提供的 Firebase 配置
        const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : {};
        const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-app-id';

        try {
            const app = initializeApp(firebaseConfig);
            db = getFirestore(app);
            auth = getAuth(app);
        } catch (e) {
            console.error("Firebase 初始化失败:", e);
            loadingOverlay.innerHTML = '<p class="text-red-500">无法连接到数据库，请检查配置。</p>';
            return;
        }

        // --- Firebase 数据处理 ---
        /**
         * 将项目数据写入 Firestore
         */
        async function saveDataToFirestore() {
            if (!userId) return;
            try {
                const projectDocRef = doc(db, `artifacts/${appId}/public/data/kanban-data`, 'main_project');
                await setDoc(projectDocRef, { projectData, qaData });
            } catch (error) {
                console.error("写入 Firestore 失败: ", error);
            }
        }

        /**
         * 监听 Firestore 数据变化
         */
        function listenForDataChanges() {
            if (!userId) return;
            const projectDocRef = doc(db, `artifacts/${appId}/public/data/kanban-data`, 'main_project');
            
            // 取消之前的监听
            if (dataUnsubscribe) dataUnsubscribe();

            dataUnsubscribe = onSnapshot(projectDocRef, (docSnap) => {
                if (docSnap.exists()) {
                    const data = docSnap.data();
                    projectData = data.projectData || {};
                    qaData = data.qaData || [];
                } else {
                    // 如果文档不存在，则用默认数据初始化
                    console.log("文档不存在，正在使用默认数据初始化...");
                    initializeDefaultData();
                    saveDataToFirestore(); // 保存到 Firestore
                }
                // 收到数据后渲染看板
                generateBoard();
                loadingOverlay.classList.add('hidden');
            }, (error) => {
                console.error("监听数据失败: ", error);
                loadingOverlay.innerHTML = '<p class="text-red-500">数据监听失败，请刷新页面重试。</p>';
            });
        }
        
        // --- 认证逻辑 ---
        onAuthStateChanged(auth, async (user) => {
            if (user) {
                // 用户已登录
                userId = user.uid;
                listenForDataChanges();
            } else {
                // 用户未登录，尝试使用自定义 Token 或匿名登录
                try {
                    if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) {
                        await signInWithCustomToken(auth, __initial_auth_token);
                    } else {
                        await signInAnonymously(auth);
                    }
                } catch (error) {
                    console.error("登录失败:", error);
                    loadingOverlay.innerHTML = '<p class="text-red-500">认证失败，无法加载应用。</p>';
                }
            }
        });

        
        function initializeDefaultData() {
            const rawData = {
                "phase-1": {
                    title: "阶段一：基础架构", color: "indigo",
                    cards: [
                        { id: crypto.randomUUID(), title: "一、企业微信基础配置", tasks: ["企业微信注册与认证", "运营人员权限配置", "组织架构搭建", "安全策略及合规性配置"] },
                        { id: crypto.randomUUID(), title: "二、资源梳理和盘点", tasks: ["运营目标设定", "客户运营方案", "SCRM系统选型"] },
                    ]
                },
                "phase-2": {
                    title: "阶段二：落地执行", color: "amber",
                    cards: [
                        { id: crypto.randomUUID(), title: "一、企微加好友", tasks: ["落地培训", "奖惩机制", "数据指标"] },
                        { id: crypto.randomUUID(), title: "二、营销资料准备", tasks: ["营销物料盘点", "话术库"] },
                    ]
                },
                "phase-3": {
                    title: "阶段三：用户经营", color: "rose",
                    cards: [
                        { id: crypto.randomUUID(), title: "一、客户管理体系建设", tasks: ["意向客户跟进", "到店客户登记", "会员运营体系"] },
                    ]
                }
            };
            
            projectData = {};
            Object.keys(rawData).forEach(phaseKey => {
                projectData[phaseKey] = { ...rawData[phaseKey], cards: [] };
                rawData[phaseKey].cards.forEach((card) => {
                    const newCard = { ...card, tasks: [] };
                    card.tasks.forEach((taskTitle) => {
                        const today = new Date();
                        const startDate = new Date(today.setDate(today.getDate() + Math.floor(Math.random() * 10))).toISOString().split('T')[0];
                        newCard.tasks.push({
                            id: crypto.randomUUID(), title: taskTitle, status: 'not-started', 
                            assignee: '', startDate: startDate, duration: Math.floor(Math.random() * 5) + 1,
                        });
                    });
                    projectData[phaseKey].cards.push(newCard);
                });
            });
            qaData = []; // 同时初始化 QA 数据
        }

        function generateBoard() {
            projectBoard.innerHTML = ''; 
            for (const phaseKey in projectData) {
                const phase = projectData[phaseKey];
                const section = document.createElement('section');
                section.id = phaseKey;
                section.innerHTML = `
                    <div class="flex justify-between items-center mb-4">
                        <h2 class="text-2xl font-bold text-gray-800 border-l-4 border-${phase.color}-500 pl-4 flex items-center gap-2">
                            <span class="phase-title-text">${phase.title}</span>
                            <button class="edit-phase-btn p-1 rounded-full hover:bg-gray-200 text-gray-400 hover:text-gray-600">
                                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Z" /></svg>
                            </button>
                        </h2>
                        <div class="add-node-container">
                            <button class="add-node-btn bg-white border border-dashed border-gray-400 text-gray-600 px-3 py-1 rounded-md text-sm hover:bg-gray-100 hover:border-solid" data-phase-key="${phaseKey}">+ 添加业务节点</button>
                        </div>
                    </div>
                    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"></div>`;
                const grid = section.querySelector('.grid');

                phase.cards.forEach((cardData) => {
                    const cardEl = document.createElement('div');
                    cardEl.className = "task-card bg-white rounded-lg shadow-md p-5 border border-gray-200 hover:shadow-xl transition-shadow duration-300 flex flex-col";
                    cardEl.dataset.cardId = cardData.id;
                    
                    let tasksHtml = '';
                    cardData.tasks.forEach(task => { tasksHtml += createTaskItemHtml(task); });

                    cardEl.innerHTML = `
                        <div class="flex-grow">
                            <h3 class="text-lg font-semibold text-gray-900 mb-3 flex justify-between items-center gap-2">
                                <span class="card-title-text flex-1">${cardData.title}</span>
                                <button class="edit-card-btn p-1 rounded-full hover:bg-gray-200 text-gray-400 hover:text-gray-600">
                                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Z" /></svg>
                                </button>
                                <button class="delete-card-btn p-1 rounded-full hover:bg-gray-200 text-gray-400 hover:text-gray-600">
                                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.134-2.033-2.134H8.033C6.91 2.75 6 3.704 6 4.866v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" /></svg>
                                </button>
                            </h3>
                            <div class="mb-4">
                                <div class="w-full progress-bar-bg rounded-full h-2.5"><div class="progress-bar bg-gradient-to-r from-green-400 to-teal-500 h-2.5 rounded-full progress-bar-fill" style="width: 0%"></div></div>
                            </div>
                            <ul class="task-list space-y-3">${tasksHtml}</ul>
                        </div>`;
                    grid.appendChild(cardEl);
                });
                projectBoard.appendChild(section);
            }
            updateAllProgress();
        }

        function createTaskItemHtml(task) {
            return `<li class="task-item-title bg-gray-50 hover:bg-gray-100 rounded-md p-2 border-l-4 ${statusColors[task.status]}" data-task-id="${task.id}">
                        <p class="font-medium text-gray-800 pointer-events-none">${task.title} <span class="text-xs text-gray-500">${task.assignee ? `(${task.assignee})` : ''}</span></p>
                    </li>`;
        }
        
        function updateAllProgress() {
            document.querySelectorAll('.task-card').forEach(updateCardProgress);
            updateTotalProgress();
            updateTotalDurationDisplay();
            renderMasterGanttChart();
            renderQAList();
        }

        function updateCardProgress(card) {
            const cardId = card.dataset.cardId;
            const cardData = findCardById(cardId);
            if (!cardData || cardData.tasks.length === 0) {
                card.querySelector('.progress-bar').style.width = '0%';
                return;
            };
            let completedCount = cardData.tasks.filter(t => t.status === 'completed').length;
            const progress = (completedCount / cardData.tasks.length) * 100;
            card.querySelector('.progress-bar').style.width = progress + '%';
        }

        function updateTotalProgress() {
            const allTasks = Object.values(projectData).flatMap(p => p.cards).flatMap(c => c.tasks);
            if (allTasks.length === 0) {
                 document.getElementById('total-progress-bar').style.width = '0%';
                 document.getElementById('total-progress-text').textContent = '0.0%';
                 return;
            };
            const completedTasks = allTasks.filter(t => t.status === 'completed');
            const totalProgress = (completedTasks.length / allTasks.length) * 100;
            document.getElementById('total-progress-bar').style.width = totalProgress + '%';
            document.getElementById('total-progress-text').textContent = totalProgress.toFixed(1) + '%';
        }

        function updateTotalDurationDisplay() {
            const allTasks = Object.values(projectData).flatMap(p => p.cards).flatMap(c => c.tasks);
            const validTasks = allTasks.filter(t => t.startDate && t.duration > 0);

            if (validTasks.length === 0) {
                document.getElementById('total-duration-text').textContent = '- 天';
                return;
            }

            let minDate = new Date(validTasks[0].startDate);
            let maxDate = new Date(validTasks[0].startDate);
            maxDate.setDate(maxDate.getDate() + parseInt(validTasks[0].duration, 10));

            validTasks.forEach(task => {
                const start = new Date(task.startDate);
                const end = new Date(task.startDate);
                end.setDate(end.getDate() + parseInt(task.duration, 10));
                if (start < minDate) minDate = start;
                if (end > maxDate) maxDate = end;
            });

            const duration = Math.ceil((maxDate - minDate) / (1000 * 60 * 60 * 24));
            document.getElementById('total-duration-text').textContent = `${duration} 天`;
        }

        function findTaskById(taskId) {
            for (const phaseKey in projectData) {
                for (const card of projectData[phaseKey].cards) {
                    const task = card.tasks.find(t => t.id === taskId);
                    if (task) return task;
                }
            }
            return null;
        }
        
        function findCardById(cardId) {
            for (const phaseKey in projectData) {
                const card = projectData[phaseKey].cards.find(c => c.id === cardId);
                if (card) return card;
            }
            return null;
        }

        function openCardModal(cardId) {
            const card = findCardById(cardId);
            if (!card) return;
            cardModal.dataset.currentCardId = cardId;
            modalCardTitle.textContent = card.title;
            renderDetailedGanttView(card.tasks);
            cardModal.classList.remove('hidden');
        }

        function closeCardModal() {
            cardModal.classList.add('hidden');
            cardModal.dataset.currentCardId = '';
            // 不再需要手动调用 generateBoard，因为 onSnapshot 会处理
        }

        function renderDetailedGanttView(tasks) {
            modalContentArea.innerHTML = '';
            const container = document.createElement('div');
            container.className = 'h-full flex flex-col space-y-4';
            
            container.innerHTML = `
                <div>
                    <div class="flex justify-between items-center mb-2">
                        <h4 class="text-lg font-semibold text-gray-700">任务清单</h4>
                        <button id="modal-add-task-btn" class="bg-indigo-600 text-white px-3 py-1.5 rounded-md text-sm font-medium hover:bg-indigo-700 flex items-center gap-1.5">
                            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" /></svg>
                            添加新任务
                        </button>
                    </div>
                    <div id="modal-task-list" class="bg-white border border-gray-200 rounded-lg"></div>
                </div>

                <div class="flex-grow flex flex-col">
                    <h4 class="text-lg font-semibold text-gray-700 mb-2">甘特图</h4>
                    <div id="gantt-view" class="gantt-chart-container flex-grow"></div>
                </div>
            `;
            
            modalContentArea.appendChild(container);
            renderGanttChart(tasks, document.getElementById('gantt-view'), document.getElementById('modal-task-list'));
        }
        
        function renderGanttChart(tasks, ganttContainer, taskListContainer) {
            ganttContainer.innerHTML = '';
            taskListContainer.innerHTML = '';
            
            const dayWidth = 40;
            const rowHeight = 44; 

            const validTasks = tasks.filter(t => t.startDate && t.duration > 0);
            
            let minDate, maxDate;
            if (validTasks.length > 0) {
                minDate = new Date(validTasks[0].startDate);
                maxDate = new Date(validTasks[0].startDate);
                maxDate.setDate(maxDate.getDate() + parseInt(validTasks[0].duration, 10));

                validTasks.forEach(task => {
                    const start = new Date(task.startDate);
                    const end = new Date(task.startDate);
                    end.setDate(end.getDate() + parseInt(task.duration, 10));
                    if (start < minDate) minDate = start;
                    if (end > maxDate) maxDate = end;
                });

                minDate.setDate(minDate.getDate() - 5);
                maxDate.setDate(maxDate.getDate() + 10);
            } else {
                minDate = new Date();
                maxDate = new Date();
                maxDate.setDate(maxDate.getDate() + 30);
            }

            const totalDays = Math.ceil((maxDate - minDate) / (1000 * 60 * 60 * 24));
            
            // Render Timeline Header
            const timelineHeader = document.createElement('div');
            timelineHeader.className = 'gantt-timeline sticky top-0 z-10 bg-gray-100';
            timelineHeader.style.gridTemplateColumns = `repeat(${totalDays}, ${dayWidth}px)`;
            
            for (let d = new Date(minDate); d < maxDate; d.setDate(d.getDate() + 1)) {
                const dayEl = document.createElement('div');
                dayEl.className = 'gantt-day';
                if (d.getDay() === 0 || d.getDay() === 6) dayEl.classList.add('weekend');
                dayEl.innerHTML = `${d.getMonth()+1}/${d.getDate()}`;
                timelineHeader.appendChild(dayEl);
            }
            ganttContainer.appendChild(timelineHeader);

            // Render Task List and Gantt Rows
            const timelineGrid = document.createElement('div');
            timelineGrid.style.position = 'relative';
            timelineGrid.style.width = `${totalDays * dayWidth}px`;
            
            tasks.forEach(task => {
                // Task Editor Row
                const taskEl = document.createElement('div');
                taskEl.className = 'task-editor-grid';
                taskEl.dataset.taskId = task.id;
                taskEl.innerHTML = `
                    <input type="text" value="${task.title}" class="task-title-input border-gray-300 rounded-md shadow-sm text-sm p-1.5" placeholder="任务标题">
                    <input type="text" value="${task.assignee}" class="task-assignee-input border-gray-300 rounded-md shadow-sm text-sm p-1.5" placeholder="执行人">
                    <div class="flex items-center gap-1">
                        <input type="date" value="${task.startDate}" class="task-start-date-input border-gray-300 rounded-md shadow-sm text-sm p-1.5 w-full">
                        <input type="number" value="${task.duration}" min="1" class="task-duration-input border-gray-300 rounded-md shadow-sm text-sm p-1.5 w-16" placeholder="天数">
                        <span class="text-sm text-gray-500">天</span>
                    </div>
                    <select class="task-status-select border-gray-300 rounded-md shadow-sm text-sm p-1.5">
                        <option value="not-started" ${task.status === 'not-started' ? 'selected' : ''}>未开始</option>
                        <option value="in-progress" ${task.status === 'in-progress' ? 'selected' : ''}>进行中</option>
                        <option value="completed" ${task.status === 'completed' ? 'selected' : ''}>已完成</option>
                        <option value="blocked" ${task.status === 'blocked' ? 'selected' : ''}>遇到问题</option>
                    </select>
                    <button class="delete-task-btn p-2 rounded-md hover:bg-red-100 text-gray-500 hover:text-red-600 flex justify-center items-center">
                        <svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
                    </button>
                `;
                taskListContainer.appendChild(taskEl);

                // Gantt Timeline Row
                const rowEl = document.createElement('div');
                rowEl.className = 'gantt-row';
                rowEl.style.height = `${rowHeight}px`;
                
                if (task.startDate && task.duration > 0) {
                    const taskStart = new Date(task.startDate);
                    const offsetDays = (taskStart - minDate) / (1000 * 60 * 60 * 24);
                    const timelineStatus = getTaskTimelineStatus(task);

                    const barEl = document.createElement('div');
                    barEl.className = `gantt-bar ${timelineStatus.isOverdue ? 'gantt-bar-status-overdue' : (ganttBarColors[task.status] || 'bg-gray-500')}`;
                    barEl.style.left = `${offsetDays * dayWidth}px`;
                    barEl.style.width = `${task.duration * dayWidth}px`;
                    barEl.textContent = task.title;
                    barEl.title = `${task.title} - ${task.duration}天`;
                    rowEl.appendChild(barEl);
                }
                timelineGrid.appendChild(rowEl);
            });

            ganttContainer.appendChild(timelineGrid);
            
            // Add Today Marker
            const today = new Date();
            today.setHours(0,0,0,0);
            if (today >= minDate && today < maxDate) {
                const offsetDays = (today - minDate) / (1000 * 60 * 60 * 24);
                const todayMarker = document.createElement('div');
                todayMarker.className = 'gantt-today-marker';
                todayMarker.style.left = `${offsetDays * dayWidth}px`;
                timelineGrid.appendChild(todayMarker);
            }
        }

        function getTaskTimelineStatus(task) {
            if (task.status === 'completed') {
                return { text: '已完成', isOverdue: false };
            }
            if (!task.startDate) {
                return { text: '-', isOverdue: false };
            }
            const today = new Date();
            today.setHours(0,0,0,0);
            const endDate = new Date(task.startDate);
            endDate.setDate(endDate.getDate() + parseInt(task.duration, 10));
            
            if (today > endDate) {
                return { text: '超时', isOverdue: true };
            }
            return { text: '正常', isOverdue: false };
        }

        function renderMasterGanttChart() {
            masterGanttContainer.innerHTML = '';
            const allTasks = Object.values(projectData).flatMap(p => p.cards).flatMap(c => c.tasks);
            const validTasks = allTasks.filter(t => t.startDate && t.duration > 0);

            if (validTasks.length === 0) {
                masterGanttContainer.innerHTML = `<p class="text-gray-500 text-sm">请为任务设置开始日期和执行天数以生成总览图。</p>`;
                return;
            }

            const dayWidth = 20;
            let projectMinDate = new Date(validTasks[0].startDate);
            let projectMaxDate = new Date(validTasks[0].startDate);

            validTasks.forEach(task => {
                const start = new Date(task.startDate);
                const end = new Date(task.startDate);
                end.setDate(end.getDate() + parseInt(task.duration, 10));
                if (start < projectMinDate) projectMinDate = start;
                if (end > projectMaxDate) projectMaxDate = end;
            });

            projectMinDate.setDate(projectMinDate.getDate() - 5);
            projectMaxDate.setDate(projectMaxDate.getDate() + 5);
            
            const totalDays = Math.ceil((projectMaxDate - projectMinDate) / (1000 * 60 * 60 * 24));

            const ganttGrid = document.createElement('div');
            ganttGrid.className = 'master-gantt-grid';
            
            const labelsCol = document.createElement('div');
            labelsCol.className = 'sticky left-0 bg-white z-10';

            const chartArea = document.createElement('div');
            
            const timelineHeader = document.createElement('div');
            timelineHeader.className = 'master-gantt-timeline';
            timelineHeader.style.gridTemplateColumns = `repeat(${totalDays}, ${dayWidth}px)`;
            
            const chartGrid = document.createElement('div');

            const labelHeader = document.createElement('div');
            labelHeader.className = 'h-10 border-b border-r border-gray-200';
            labelsCol.appendChild(labelHeader);

            for (let d = new Date(projectMinDate); d < projectMaxDate; d.setDate(d.getDate() + 1)) {
                const dayEl = document.createElement('div');
                dayEl.className = 'master-gantt-day h-10';
                if (d.getDate() === 1) {
                     dayEl.innerHTML = `${d.getMonth()+1}月`;
                     dayEl.style.fontWeight = 'bold';
                } else {
                     dayEl.innerHTML = d.getDate();
                }
                timelineHeader.appendChild(dayEl);
            }
            chartArea.appendChild(timelineHeader);

            for (const phaseKey in projectData) {
                const phase = projectData[phaseKey];
                const phaseTasks = phase.cards.flatMap(c => c.tasks).filter(t => t.startDate && t.duration > 0);
                
                const phaseLabel = document.createElement('div');
                phaseLabel.className = 'master-gantt-label master-gantt-label-phase';
                phaseLabel.textContent = phase.title;
                labelsCol.appendChild(phaseLabel);
                
                const phaseRow = document.createElement('div');
                phaseRow.className = 'master-gantt-row';
                phaseRow.style.width = `${totalDays * dayWidth}px`;

                if (phaseTasks.length > 0) {
                    let phaseMinDate = new Date(phaseTasks[0].startDate);
                    let phaseMaxDate = new Date(phaseTasks[0].startDate);
                    phaseMaxDate.setDate(phaseMaxDate.getDate() + parseInt(phaseTasks[0].duration, 10));
                    phaseTasks.forEach(task => {
                        const start = new Date(task.startDate);
                        const end = new Date(task.startDate);
                        end.setDate(end.getDate() + parseInt(task.duration, 10));
                        if (start < phaseMinDate) phaseMinDate = start;
                        if (end > phaseMaxDate) phaseMaxDate = end;
                    });

                    const phaseOffsetDays = (phaseMinDate - projectMinDate) / (1000 * 60 * 60 * 24);
                    const phaseDurationDays = (phaseMaxDate - phaseMinDate) / (1000 * 60 * 60 * 24);

                    const phaseBarEl = document.createElement('div');
                    phaseBarEl.className = `master-gantt-bar master-gantt-bar-phase`;
                    phaseBarEl.style.left = `${phaseOffsetDays * dayWidth}px`;
                    phaseBarEl.style.width = `${phaseDurationDays * dayWidth}px`;
                    phaseBarEl.style.backgroundColor = phase.color === 'amber' ? '#f59e0b' : (phase.color === 'rose' ? '#f43f5e' : '#4f46e5');
                    phaseBarEl.title = `${phase.title} - ${Math.round(phaseDurationDays)}天`;
                    phaseRow.appendChild(phaseBarEl);
                }
                chartArea.appendChild(phaseRow);

                phase.cards.forEach(card => {
                    const cardTasks = card.tasks.filter(t => t.startDate && t.duration > 0);
                    
                    const cardLabel = document.createElement('div');
                    cardLabel.className = 'master-gantt-label master-gantt-label-card';
                    cardLabel.textContent = card.title;
                    labelsCol.appendChild(cardLabel);
                    
                    const cardRow = document.createElement('div');
                    cardRow.className = 'master-gantt-row';
                    cardRow.style.width = `${totalDays * dayWidth}px`;

                    if (cardTasks.length > 0) {
                        let cardMinDate = new Date(cardTasks[0].startDate);
                        let cardMaxDate = new Date(cardTasks[0].startDate);
                        cardMaxDate.setDate(cardMaxDate.getDate() + parseInt(cardTasks[0].duration, 10));
                        cardTasks.forEach(task => {
                            const start = new Date(task.startDate);
                            const end = new Date(task.startDate);
                            end.setDate(end.getDate() + parseInt(task.duration, 10));
                            if (start < cardMinDate) cardMinDate = start;
                            if (end > cardMaxDate) cardMaxDate = end;
                        });
                        
                        const offsetDays = (cardMinDate - projectMinDate) / (1000 * 60 * 60 * 24);
                        const durationDays = (cardMaxDate - cardMinDate) / (1000 * 60 * 60 * 24);
                        
                        const barEl = document.createElement('div');
                        barEl.className = `master-gantt-bar`;
                        barEl.style.left = `${offsetDays * dayWidth}px`;
                        barEl.style.width = `${durationDays * dayWidth}px`;
                        barEl.style.backgroundColor = phase.color === 'amber' ? '#fcd34d' : (phase.color === 'rose' ? '#fda4af' : '#a5b4fc');
                        barEl.title = `${card.title} - ${Math.round(durationDays)}天`;
                        cardRow.appendChild(barEl);
                    }
                    chartArea.appendChild(cardRow);
                });
            }
            
            ganttGrid.appendChild(labelsCol);
            ganttGrid.appendChild(chartArea);
            masterGanttContainer.appendChild(ganttGrid);
        }
        
        function openPasswordModal(action, id) {
            if (action === 'importExcel') {
                passwordModalTitle.textContent = '请输入密码以导入数据';
                passwordConfirmBtn.textContent = '确认导入';
                passwordConfirmBtn.classList.remove('bg-red-600', 'hover:bg-red-700');
                passwordConfirmBtn.classList.add('bg-indigo-600', 'hover:bg-indigo-700');
            } else if (action === 'resetData') {
                passwordModalTitle.textContent = '确认重置所有数据？此操作不可撤销。';
                passwordConfirmBtn.textContent = '确认重置';
                passwordConfirmBtn.classList.remove('bg-indigo-600', 'hover:bg-indigo-700');
                passwordConfirmBtn.classList.add('bg-red-600', 'hover:bg-red-700');
            } else { // for delete actions
                passwordModalTitle.textContent = '请输入密码以确认删除';
                passwordConfirmBtn.textContent = '确认删除';
                passwordConfirmBtn.classList.remove('bg-indigo-600', 'hover:bg-indigo-700');
                passwordConfirmBtn.classList.add('bg-red-600', 'hover:bg-red-700');
            }
            passwordModal.classList.remove('hidden');
            passwordInput.value = '';
            passwordInput.focus();
            passwordError.classList.add('hidden');
            passwordModal.dataset.action = action;
            passwordModal.dataset.id = id;
        }

        function closePasswordModal() {
            passwordModal.classList.add('hidden');
        }

        // --- Event Listeners ---
        projectBoard.addEventListener('click', function(e) {
            const cardHeader = e.target.closest('.task-card h3');
            if (cardHeader && !e.target.closest('.edit-card-btn') && !e.target.closest('.delete-card-btn')) {
                const cardId = cardHeader.closest('.task-card').dataset.cardId;
                openCardModal(cardId);
                return;
            }
            
            const addNodeBtn = e.target.closest('.add-node-btn');
            if (addNodeBtn) {
                const container = addNodeBtn.parentElement;
                container.innerHTML = `
                    <form class="add-node-form flex gap-2 items-center">
                        <input type="text" placeholder="新业务节点标题" class="flex-grow border-gray-300 rounded-md shadow-sm text-sm" required>
                        <button type="submit" class="bg-indigo-600 text-white px-3 py-1 rounded-md text-sm">保存</button>
                        <button type="button" class="cancel-add-btn text-gray-500 hover:text-gray-700 text-sm">取消</button>
                    </form>`;
                container.querySelector('input').focus();
                return;
            }

            const cancelAddBtn = e.target.closest('.cancel-add-btn');
            if (cancelAddBtn) {
                generateBoard();
                return;
            }

            const editCardBtn = e.target.closest('.edit-card-btn');
            if (editCardBtn) {
                const h3 = editCardBtn.closest('h3');
                const titleSpan = h3.querySelector('.card-title-text');
                const currentTitle = titleSpan.textContent;
                titleSpan.innerHTML = `<form class="edit-card-form"><input type="text" class="w-full border-b-2 border-indigo-300 focus:outline-none" value="${currentTitle}"></form>`;
                const input = titleSpan.querySelector('input');
                input.focus();
                input.select();
                return;
            }

            const deleteCardBtn = e.target.closest('.delete-card-btn');
            if(deleteCardBtn) {
                const cardId = deleteCardBtn.closest('.task-card').dataset.cardId;
                openPasswordModal('deleteCard', cardId);
            }

            const editPhaseBtn = e.target.closest('.edit-phase-btn');
            if (editPhaseBtn) {
                const h2 = editPhaseBtn.closest('h2');
                const titleSpan = h2.querySelector('.phase-title-text');
                const currentTitle = titleSpan.textContent;
                titleSpan.innerHTML = `<form class="edit-phase-form"><input type="text" class="w-full text-2xl font-bold border-b-2 border-indigo-300 focus:outline-none" value="${currentTitle}"></form>`;
                const input = titleSpan.querySelector('input');
                input.focus();
                input.select();
            }
        });

        projectBoard.addEventListener('submit', function(e) {
            e.preventDefault();
            if (e.target.classList.contains('add-node-form')) {
                const form = e.target;
                const input = form.querySelector('input');
                const title = input.value.trim();
                if (title) {
                    const phaseKey = form.closest('section').id;
                    projectData[phaseKey].cards.push({ id: crypto.randomUUID(), title: title, tasks: [] });
                    saveDataToFirestore();
                }
            } else if (e.target.classList.contains('edit-card-form')) {
                 const form = e.target;
                 const input = form.querySelector('input');
                 const newTitle = input.value.trim();
                 const cardId = form.closest('.task-card').dataset.cardId;
                 const card = findCardById(cardId);
                 if (card && newTitle) {
                     card.title = newTitle;
                 }
                 saveDataToFirestore();
            } else if (e.target.classList.contains('edit-phase-form')) {
                const form = e.target;
                const input = form.querySelector('input');
                const newTitle = input.value.trim();
                const phaseKey = form.closest('section').id;
                if (projectData[phaseKey] && newTitle) {
                    projectData[phaseKey].title = newTitle;
                }
                saveDataToFirestore();
            }
        });
        
        projectBoard.addEventListener('focusout', function(e) {
             if (e.target.parentElement && e.target.parentElement.classList.contains('edit-card-form')) {
                 const form = e.target.parentElement;
                 const newTitle = e.target.value.trim();
                 const cardId = form.closest('.task-card').dataset.cardId;
                 const card = findCardById(cardId);
                 if (card && newTitle) {
                     card.title = newTitle;
                 }
                 saveDataToFirestore();
             } else if (e.target.parentElement && e.target.parentElement.classList.contains('edit-phase-form')) {
                const form = e.target.parentElement;
                const newTitle = e.target.value.trim();
                const phaseKey = form.closest('section').id;
                if (projectData[phaseKey] && newTitle) {
                    projectData[phaseKey].title = newTitle;
                }
                saveDataToFirestore();
             }
        });

        modalCardCloseBtn.addEventListener('click', closeCardModal);
        cardModal.addEventListener('click', (e) => { 
            if (e.target === cardModal) {
                 closeCardModal();
            }
        });
        
        modalContentArea.addEventListener('click', (e) => {
            if (e.target.closest('#modal-add-task-btn')) {
                const cardId = cardModal.dataset.currentCardId;
                const card = findCardById(cardId);
                if (card) {
                    card.tasks.push({
                        id: crypto.randomUUID(), title: '新任务', status: 'not-started',
                        assignee: '', startDate: new Date().toISOString().split('T')[0], duration: 1,
                    });
                    saveDataToFirestore();
                }
            } else if (e.target.closest('.delete-task-btn')) {
                const taskRow = e.target.closest('.task-editor-grid');
                const taskId = taskRow.dataset.taskId;
                openPasswordModal('deleteTask', taskId);
            }
        });

        modalContentArea.addEventListener('change', (e) => {
            const input = e.target;
            const taskRow = input.closest('.task-editor-grid');
            if (!taskRow) return;
            const taskId = taskRow.dataset.taskId;
            const task = findTaskById(taskId);
            if (!task) return;

            if (input.classList.contains('task-title-input')) task.title = input.value;
            if (input.classList.contains('task-assignee-input')) task.assignee = input.value;
            if (input.classList.contains('task-start-date-input')) task.startDate = input.value;
            if (input.classList.contains('task-duration-input')) task.duration = parseInt(input.value, 10) || 1;
            if (input.classList.contains('task-status-select')) task.status = input.value;
            
            saveDataToFirestore();
        });

        passwordForm.addEventListener('submit', (e) => {
            e.preventDefault();
            const password = passwordInput.value;
            if (password === DELETE_PASSWORD) {
                const action = passwordModal.dataset.action;
                const id = passwordModal.dataset.id;
                
                if (action === 'deleteCard') {
                    for (const phaseKey in projectData) {
                        projectData[phaseKey].cards = projectData[phaseKey].cards.filter(c => c.id !== id);
                    }
                } else if (action === 'deleteTask') {
                    const cardId = cardModal.dataset.currentCardId;
                    const card = findCardById(cardId);
                    if (card) {
                        card.tasks = card.tasks.filter(t => t.id !== id);
                    }
                } else if (action === 'importExcel') {
                    fileInput.click();
                } else if (action === 'deleteQA') {
                    qaData = qaData.filter(qa => qa.id !== id);
                } else if (action === 'resetData') {
                    initializeDefaultData();
                }
                saveDataToFirestore();
                closePasswordModal();
            } else {
                passwordError.classList.remove('hidden');
            }
        });
        
        passwordCancelBtn.addEventListener('click', closePasswordModal);
        
        exportBtn.addEventListener('click', () => {
            const dataToExport = [];
            for (const phaseKey in projectData) {
                const phase = projectData[phaseKey];
                phase.cards.forEach(card => {
                    card.tasks.forEach(task => {
                        dataToExport.push({
                            '阶段': phase.title,
                            '业务节点': card.title,
                            '任务': task.title,
                            '执行人': task.assignee,
                            '开始日期': task.startDate,
                            '执行天数': task.duration,
                            '状态': task.status
                        });
                    });
                });
            }
            const worksheet = XLSX.utils.json_to_sheet(dataToExport);
            const workbook = XLSX.utils.book_new();
            XLSX.utils.book_append_sheet(workbook, worksheet, "项目数据");
            XLSX.writeFile(workbook, "project_data.xlsx");
        });

        importBtn.addEventListener('click', () => {
            openPasswordModal('importExcel');
        });

        fileInput.addEventListener('change', (event) => {
            const file = event.target.files[0];
            if (!file) return;

            const reader = new FileReader();
            reader.onload = (e) => {
                try {
                    const data = new Uint8Array(e.target.result);
                    const workbook = XLSX.read(data, {type: 'array'});
                    const firstSheetName = workbook.SheetNames[0];
                    const worksheet = workbook.Sheets[firstSheetName];
                    const json = XLSX.utils.sheet_to_json(worksheet);
                    
                    const newProjectData = {};
                    json.forEach(row => {
                        const phaseTitle = row['阶段'];
                        const cardTitle = row['业务节点'];
                        const taskTitle = row['任务'];

                        if (!phaseTitle || !cardTitle || !taskTitle) return;

                        let phaseKey = Object.keys(newProjectData).find(k => newProjectData[k].title === phaseTitle);
                        if (!phaseKey) {
                            const originalPhase = Object.values(projectData).find(p => p.title === phaseTitle);
                            phaseKey = `phase-imported-${crypto.randomUUID()}`;
                            newProjectData[phaseKey] = { title: phaseTitle, color: originalPhase ? originalPhase.color : 'gray', cards: [] };
                        }

                        let card = newProjectData[phaseKey].cards.find(c => c.title === cardTitle);
                        if (!card) {
                            card = { id: crypto.randomUUID(), title: cardTitle, tasks: [] };
                            newProjectData[phaseKey].cards.push(card);
                        }

                        card.tasks.push({
                            id: crypto.randomUUID(),
                            title: taskTitle,
                            assignee: row['执行人'] || '',
                            startDate: row['开始日期'] || '',
                            duration: row['执行天数'] || 1,
                            status: row['状态'] || 'not-started'
                        });
                    });
                    projectData = newProjectData;
                    qaData = []; // 导入时重置QA数据
                    saveDataToFirestore();
                } catch (error) {
                    console.error("Error processing Excel file:", error);
                    alert("导入失败，请确保文件格式正确。");
                } finally {
                    fileInput.value = '';
                }
            };
            reader.readAsArrayBuffer(file);
        });
        
        resetDataBtn.addEventListener('click', () => {
             openPasswordModal('resetData');
        });

        // Q&A Logic
        function renderQAList() {
            const filterText = qaSearchInput.value.toLowerCase();
            const filterStatus = qaFilterSelect.value;

            let filteredQA = qaData.filter(qa => {
                const matchesText = qa.question.toLowerCase().includes(filterText) || (qa.answer && qa.answer.toLowerCase().includes(filterText));
                const matchesStatus = filterStatus === 'all' || (filterStatus === 'resolved' && qa.answer) || (filterStatus === 'unresolved' && !qa.answer);
                return matchesText && matchesStatus;
            });

            qaList.innerHTML = '';
            const totalPages = Math.ceil(filteredQA.length / qaItemsPerPage);
            const start = (qaCurrentPage - 1) * qaItemsPerPage;
            const end = start + qaItemsPerPage;
            const paginatedItems = filteredQA.slice(start, end);

            if (paginatedItems.length === 0) {
                qaList.innerHTML = `<p class="text-gray-500 text-sm">${filterText || filterStatus !== 'all' ? '未找到匹配的疑问。' : '暂无疑问，点击下方按钮提出一个。'}</p>`;
            }
            paginatedItems.forEach(qa => {
                const task = findTaskById(qa.taskId);
                let taskPath = '未找到关联任务';
                if (task) {
                    for (const phaseKey in projectData) {
                        for (const card of projectData[phaseKey].cards) {
                            if (card.tasks.some(t => t.id === task.id)) {
                                taskPath = `${projectData[phaseKey].title} / ${card.title} / ${task.title}`;
                                break;
                            }
                        }
                    }
                }

                const qaEl = document.createElement('div');
                qaEl.className = 'bg-gray-50 p-4 rounded-lg border';
                qaEl.dataset.qaId = qa.id;
                const questionColor = qa.answer ? 'text-gray-800' : 'text-red-600';

                qaEl.innerHTML = `
                    <div class="flex justify-between items-start">
                        <div>
                            <p class="font-semibold ${questionColor}">${qa.question}</p>
                            <p class="text-xs text-gray-500 mt-1">
                                <span class="font-medium">${qa.questionDate}</span> | 关联任务: <span class="font-medium text-indigo-600">${taskPath}</span>
                            </p>
                        </div>
                        <button class="delete-qa-btn p-1 rounded-full hover:bg-gray-200 text-gray-400 hover:text-gray-600">
                           <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.134-2.033-2.134H8.033C6.91 2.75 6 3.704 6 4.866v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" /></svg>
                        </button>
                    </div>
                    <div class="mt-3 pl-4 border-l-2 border-gray-200 answer-section">
                        ${qa.answer ? 
                        `<div class="space-y-1">
                            <p class="text-gray-700 text-sm">${qa.answer}</p>
                            <div class="flex items-center gap-2">
                                <p class="text-xs text-gray-500">${qa.answerDate}</p>
                                <button class="edit-answer-btn text-xs text-indigo-600 hover:text-indigo-800">修改答案</button>
                            </div>
                         </div>` : 
                        `<div class="add-answer-container">
                            <button class="add-answer-btn text-sm text-green-600 hover:text-green-800">添加答疑</button>
                         </div>`
                        }
                    </div>
                `;
                qaList.appendChild(qaEl);
            });
            renderQAPagination(totalPages, filteredQA.length);
        }

        function renderQAPagination(totalPages) {
            qaPagination.innerHTML = '';
            if (totalPages <= 1) return;

            for (let i = 1; i <= totalPages; i++) {
                const pageBtn = document.createElement('button');
                pageBtn.textContent = i;
                pageBtn.className = `px-3 py-1 rounded-md text-sm ${i === qaCurrentPage ? 'bg-indigo-600 text-white' : 'bg-white text-gray-700 hover:bg-gray-100'}`;
                pageBtn.addEventListener('click', () => {
                    qaCurrentPage = i;
                    renderQAList();
                });
                qaPagination.appendChild(pageBtn);
            }
        }

        qaSearchInput.addEventListener('input', () => {
            qaCurrentPage = 1;
            renderQAList();
        });
        
        qaFilterSelect.addEventListener('change', () => {
            qaCurrentPage = 1;
            renderQAList();
        });

        qaSection.addEventListener('click', (e) => {
            if (e.target.closest('#add-qa-btn')) {
                const container = addQAContainer;
                container.innerHTML = `
                    <form id="add-qa-form" class="p-4 border rounded-lg bg-gray-50 space-y-3">
                        <div>
                            <label for="qa-question" class="block text-sm font-medium text-gray-700">疑问内容</label>
                            <textarea id="qa-question" required class="mt-1 block w-full border-gray-300 rounded-md shadow-sm" rows="3"></textarea>
                        </div>
                        <div>
                            <label for="qa-task-link" class="block text-sm font-medium text-gray-700">关联任务</label>
                            <select id="qa-task-link" required class="mt-1 block w-full border-gray-300 rounded-md shadow-sm">
                                <option value="">请选择一个任务...</option>
                            </select>
                        </div>
                        <div class="flex justify-end gap-2">
                            <button type="button" id="cancel-add-qa-btn" class="bg-gray-200 text-gray-800 px-3 py-1 rounded-md text-sm">取消</button>
                            <button type="submit" class="bg-indigo-600 text-white px-3 py-1 rounded-md text-sm">保存疑问</button>
                        </div>
                    </form>
                `;
                const selectEl = document.getElementById('qa-task-link');
                for (const phaseKey in projectData) {
                    const phase = projectData[phaseKey];
                    phase.cards.forEach(card => {
                        card.tasks.forEach(task => {
                            const option = document.createElement('option');
                            option.value = task.id;
                            option.textContent = `${phase.title} / ${card.title} / ${task.title}`;
                            selectEl.appendChild(option);
                        });
                    });
                }
            } else if (e.target.closest('#cancel-add-qa-btn')) {
                addQAContainer.innerHTML = `<button id="add-qa-btn" class="bg-indigo-100 text-indigo-700 px-4 py-2 rounded-md text-sm font-medium hover:bg-indigo-200">提出新疑问</button>`;
            } else if (e.target.closest('.add-answer-btn')) {
                const container = e.target.parentElement;
                container.innerHTML = `
                    <form class="add-answer-form space-y-2">
                        <textarea required class="block w-full border-gray-300 rounded-md shadow-sm text-sm" rows="2" placeholder="请输入答疑内容..."></textarea>
                        <div class="flex justify-end gap-2">
                             <button type="button" class="cancel-add-answer-btn text-gray-500 hover:text-gray-700 text-sm">取消</button>
                             <button type="submit" class="bg-green-600 text-white px-3 py-1 rounded-md text-sm">保存答疑</button>
                        </div>
                    </form>
                `;
            } else if (e.target.closest('.cancel-add-answer-btn') || e.target.closest('.cancel-edit-answer-btn')) {
                renderQAList();
            } else if (e.target.closest('.delete-qa-btn')) {
                const qaId = e.target.closest('[data-qa-id]').dataset.qaId;
                openPasswordModal('deleteQA', qaId);
            } else if (e.target.closest('.edit-answer-btn')) {
                const answerSection = e.target.closest('.answer-section');
                const qaId = answerSection.closest('[data-qa-id]').dataset.qaId;
                const qaItem = qaData.find(qa => qa.id === qaId);
                if (qaItem) {
                    answerSection.innerHTML = `
                        <form class="edit-answer-form space-y-2">
                            <textarea required class="block w-full border-gray-300 rounded-md shadow-sm text-sm" rows="2">${qaItem.answer}</textarea>
                            <div class="flex justify-end gap-2">
                                <button type="button" class="cancel-edit-answer-btn text-gray-500 hover:text-gray-700 text-sm">取消</button>
                                <button type="submit" class="bg-green-600 text-white px-3 py-1 rounded-md text-sm">更新答案</button>
                            </div>
                        </form>
                    `;
                }
            }
        });

        qaSection.addEventListener('submit', (e) => {
            e.preventDefault();
            if (e.target.id === 'add-qa-form') {
                const question = document.getElementById('qa-question').value.trim();
                const taskId = document.getElementById('qa-task-link').value;
                if (question && taskId) {
                    qaData.unshift({
                        id: crypto.randomUUID(),
                        question,
                        taskId,
                        answer: '',
                        questionDate: new Date().toLocaleDateString('zh-CN'),
                        answerDate: ''
                    });
                    qaCurrentPage = 1;
                    saveDataToFirestore();
                }
            } else if (e.target.classList.contains('add-answer-form') || e.target.classList.contains('edit-answer-form')) {
                const qaId = e.target.closest('[data-qa-id]').dataset.qaId;
                const answer = e.target.querySelector('textarea').value.trim();
                const qaItem = qaData.find(qa => qa.id === qaId);
                if (qaItem && answer) {
                    qaItem.answer = answer;
                    qaItem.answerDate = new Date().toLocaleDateString('zh-CN');
                    saveDataToFirestore();
                }
            }
        });
        
        exportQaBtn.addEventListener('click', () => {
            const dataToExport = qaData.map(qa => {
                const task = findTaskById(qa.taskId);
                 let taskPath = 'N/A';
                if (task) {
                    for (const phaseKey in projectData) {
                        for (const card of projectData[phaseKey].cards) {
                            if (card.tasks.some(t => t.id === task.id)) {
                                taskPath = `${projectData[phaseKey].title} / ${card.title} / ${task.title}`;
                                break;
                            }
                        }
                    }
                }
                return {
                    '疑问': qa.question,
                    '提问日期': qa.questionDate,
                    '关联任务': taskPath,
                    '状态': qa.answer ? '已解决' : '未解决',
                    '答疑': qa.answer,
                    '答疑日期': qa.answerDate
                }
            });
            const worksheet = XLSX.utils.json_to_sheet(dataToExport);
            const workbook = XLSX.utils.book_new();
            XLSX.utils.book_append_sheet(workbook, worksheet, "项目疑问");
            XLSX.writeFile(workbook, "project_qa.xlsx");
        });
    });
    </script>
</body>
</html>
